package com.bxl.index;

import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.*;

/**
 * <>
 *
 * @author baixl
 * @date 2021/3/24
 */
public class TestFileIndex {

    public static void main(String[] args) {
        try {

            Map<String, Integer> map = new HashMap<>(150000);
            Date start = new Date();
            //从堆中分配内存 commitlog
            ByteBuffer bBuffer = ByteBuffer.allocate(8388608);
            //写入10万条数据
            for (int i = 0; i < 100000; i++) {
                String key = "bxl" + i;
                Msg msg = new Msg("我爱中华人民共和国" + i, key);
                Integer offset = bBuffer.position();
                //key在文件中的物理偏移量
                map.put(key, offset);
                byte[] content = msg.toByte();
                //顺序写
                bBuffer.putInt(content.length);
                bBuffer.put(content);
            }
            bBuffer.flip();
            //使用channel将内容写入文件ai-test.log commitlog
            RandomAccessFile file = new RandomAccessFile("D:\\home\\em_wf\\board\\log\\ai-test.log", "rws");
            FileChannel channel = file.getChannel();
            channel.write(bBuffer);
            channel.force(true);
            //从堆中分配内存 index
            ByteBuffer iBuffer = ByteBuffer.allocate(4300000);
            //索引条目开始写的位置 10000个hash槽，每个占4个字节 10000×4
            //hash槽的个数随机设定，没有计算。10万条数据平均下来，每个槽有10条数据，感觉数据多的话，可以增大hash槽
            int curIndex = 40000;
            //遍历map，将消息在文件中的物理偏移量写入 索引文件。 
            //索引文件格式 hash槽+索引条目
            //索引条目包含 物理偏移量和前驱
            for (Map.Entry<String, Integer> entry : map.entrySet()) {
                //根据key计算所在的hash槽
                int hash = Math.abs(entry.getKey().hashCode()) % 10000;
                //hash槽的位置
                iBuffer.position(hash * 4);
                int preOffset = iBuffer.getInt();
                Index index = null;
                //判断hash槽中是否有数据，有数据，将数据在索引中的偏移量写入索引条目
                if (preOffset <= 0) {
                    index = new Index(0, entry.getValue());
                } else {
                    index = new Index(preOffset, entry.getValue());
                }
                //跳转到索引条目
                iBuffer.position(curIndex);
                //写索引
                index.writeData(iBuffer);
                
                //将hash槽中的值改为最新的索引条目的偏移量
                iBuffer.position(hash * 4);
                iBuffer.putInt(curIndex);
                //索引条目数据增长
                curIndex = curIndex + 8;
            }
            //将buffer设置到最大长度
            iBuffer.position(curIndex);
            //转换读
            iBuffer.flip();
            //使用channel将内容写入文件ai-index.index index
            RandomAccessFile indexFile = new RandomAccessFile("D:\\home\\em_wf\\board\\log\\ai-index.index", "rws");
            FileChannel indexChannel = indexFile.getChannel();
            indexChannel.write(iBuffer);
            indexChannel.force(true);

            //bBuffer.flip();
            //随机查询1000条数据，验证生成的索引是否正确,因为是直接使用bytebuffer查询的，只能说明是写入文件索引前，生成的索引格式是正确的
            Date mod = new Date();
            System.out.println(("usetime ::" + (mod.getTime() - start.getTime())));
            for (int i = 0; i < 1000; i++) {
                Random random = new Random();
                int index = random.nextInt(100000);

                int hash = Math.abs(("bxl" + index).hashCode()) % 10000;
                iBuffer.position(hash * 4);
                int indexOffset = iBuffer.getInt();
                List<Integer> phyOffsetList = new ArrayList();
                while (indexOffset > 0) {
                    iBuffer.position(indexOffset);
                    int phyOffset = iBuffer.getInt();
                    phyOffsetList.add(phyOffset);
                    indexOffset = iBuffer.getInt();
                }

                for (int j = 0; j < phyOffsetList.size(); j++) {
                    bBuffer.position(phyOffsetList.get(j));
                    byte [] content = new byte[bBuffer.getInt()];
                    bBuffer.get(content);
                    String mes = new String(content);
                    String key = mes.split(",")[0];
                    if(key.equals(("bxl" + index))){
                        System.out.println(i+":::"+index + "::::" + new String(content));
                    }

                }
            }
            Date end = new Date();
            System.out.println((end.getTime() - mod.getTime()));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static class Msg {
        private String msg;
        private String key;
        private int number;

        public Msg(String msg, String key) {
            this.msg = msg;
            this.key = key;
        }

        public byte[] toByte() {
            return new StringBuilder(key).append(",").append(msg).toString().getBytes();

        }
    }

    public static class Index {
        private int pre;
        private int offset;

        public Index(int pre, int offset) {
            this.pre = pre;
            this.offset = offset;
        }

        public void writeData(ByteBuffer ibuffer) {
            ibuffer.putInt(offset);
            ibuffer.putInt(pre);

        }
    }
}
